package com.wh1200.xmlless.test.dao

import com.baomidou.mybatisplus.core.metadata.IPage
import com.wh1200.mybatis.xmlless.XmlLessMapper
import com.wh1200.mybatis.xmlless.annotations.*
import com.wh1200.mybatis.xmlless.enums.QueryType
import com.wh1200.xmlless.test.form.QueryForm
import com.wh1200.xmlless.test.form.StudentQueryForm
import com.wh1200.xmlless.test.model.*
import com.wh1200.xmlless.test.vo.StudentSimpleVO
import com.wh1200.xmlless.test.vo.StudentUpdateVO
import org.apache.ibatis.annotations.Mapper
import org.apache.ibatis.annotations.Param
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.*

/**
 *
 * Created by 吴昊 on 2018-12-12.
 *
 * @author 吴昊
 * @since 0.0.4
 */
@Mapper
interface StudentDAO : XmlLessMapper<Student> {

  @XmllessMethod(
      sortBy = ["createTime desc", "cast(name as char character set gbk) ASC"],
      includes = ["distinct email"]
  )
  fun select(): List<Student>

  /**
   * @return
   */
  fun selectMaxAge(): Int

  /**
   * @return
   */
  @UseEntityConvert
  fun selectUserMapper(): List<String>

  /**
   * @param page
   * @return
   */
  @XmllessMethod
  fun findAllPage2(page: IPage<Student>): IPage<Student>

  /**
   * @param name
   * @param subjectId
   * @param page
   * @return
   */
  @XmllessMethod(conditions = ["name", "subjectId"])
  fun findAllPage3(
      name: String?,
      subjectId: Int?,
      page: IPage<Student>
  ): IPage<Student>

  /**
   * @param name
   * @param subjectId
   * @param page
   * @return
   */
  fun findAllPage4(
      name: String?,
      subjectId: Int?,
      page: Pageable
  ): Page<Student>


  fun findByGraduatedTrue(): List<Student>

  /**
   * @param form
   * @param currentUserId
   * @return
   */
  @XmllessMethod(
      conditions = [
        "createTime between start and end"
      ],
      sortBy = ["createTime desc"]
  )
  fun find(
      @Param("form") form: StudentQueryForm,
      @Param("currentUserId") currentUserId: Int? = null
  ): List<Student>

  /**
   * @param form
   * @return
   */
  @XmllessMethod(
      conditions = ["birthday is not null"],
      type = QueryType.Select
  )
  fun find3(form: StudentQueryForm): List<Student>

  fun findByIdNot(@Param("id") id: Int): List<Student>

  /**
   * @param form
   * @return
   */
  @XmllessMethod(
      conditions = [
        "date_format(createTime, '%Y-%m-%d') <= date_format(endDate, '%Y-%m-%d')",
        "(name is null and age is not null) or (name is not null and age is null)"
      ],
      limit = "1"
  )
  fun find4(@Param("form") form: StudentQueryForm): List<Student>


  @XmllessMethod(
      conditions = [
        "phoneNumber like keywords",
        "name is null or name = ''"
      ]
  )
  fun find19(@Param("keywords") keywords: String): List<Student>

  @XmllessMethod(
      type = QueryType.Update,
      updateSet = [
        "graduated = true"
      ],
      conditions = ["id", "age"]
  )
  fun markGraduated(@Param("id") id: Long, @Param("age") age: Int): Int


  @XmllessMethod(
      updateSet = [
        "\$path = #value",
        "detail.\$path = #value"
      ]
  )
  fun updateByValue(@Param("path") path: String, @Param("value") value: String): List<Student>

  /**
   * @param type
   * @return
   */
  @XmllessMethod(
      type = QueryType.None
  )
  fun find5(
      @TestCriteria(
          ifExpression = "= 1",
          sqlExpression = "createUser.name = name"
      )
      @TestCriteria(
          ifExpression = "= 2",
          sqlExpression = "updateUser.name = name"
      )
      @Param("type") type: Int,
      @Param("name") name: String
  ): List<Student>

  @XmllessMethod(
      type = QueryType.None
  )
  fun find6(
      @ChooseCriteria(
          value = [
            "if = 1 then createUser.name = name",
            "if = 2 then updateUser.name = name"
          ],
          otherwise = "createUser.name is null or updateUser.name is null"
      )
      @Param("type") type: Int,
      @Param("name") name: String
  ): List<Student>

  /**
   * @param keywords
   * @return
   */
  fun findByUserNameLike(keywords: String): List<Student>

  /**
   * @param keywords
   * @return
   */
  @XmllessMethod(
      conditions = [
        "school.location like keywords"
      ]
  )
  fun findVO(@Param("keywords") keywords: String): List<StudentVO>

  @XmllessMethod(
      conditions = ["name", "age"]
  )
  fun findByNameAndAge(form: Form): List<Student>


  class Form {
    var name: String? = null
    var age: Int? = null
  }

  /**
   * @return
   */
  @XmllessMethod(
      groupBy = [
        "date(createTime)"
      ],
      includes = ["dateConfig.date"]
  )
  fun findDate(): List<Date>

  /**
   * @return
   */
  fun findCountAndGradeGroupByGrade(): List<com.wh1200.xmlless.test.dao.StudentDAO.GroupResult>

  /**
   * @param id
   * @param value
   * @return
   */
  fun updateFavoritesRemoveValueById(@Param("id") id: String, @Param("value") value: String): Int

  /**
   * @param id
   * @param value
   * @return
   */
  fun updateFavoritesAddValueById(@Param("id") id: String, @Param("value") value: String): Int

  /**
   * @return
   */
  fun findCountAndGradeGroupByGradeHavingCountGt1(): List<com.wh1200.xmlless.test.dao.StudentDAO.GroupResult>

  /**
   */
  @XmllessMethod(
      conditions = [
        "createUser.departmentId = 22"
      ],
      groupBy = [
        "date_format(createTime, '%Y-%m-%d')"
      ]
  )
  @SelectedProperties(["dateConfig.date"])
  fun findDate2()

  /**
   */
  @XmllessMethod(
      groupBy = [
        "date_format(createTime, '%Y-%m-%d')"
      ]
  )
  @SelectedProperties(["dateConfig.date"])
  fun findDate3()

  /**
   * @return
   */
  @XmllessMethod(
      type = QueryType.Select,
      groupBy = ["grade"]
  )
  fun statistics(): List<StudentStats>


  @XmllessMethod(
      type = QueryType.Select,
      groupBy = ["grade"]
  )
  fun statistics2(page: Pageable): Page<StudentStats>

  @XmllessMethod(
      type = QueryType.Select,
      groupBy = ["grade"],
      includes = [
        "grade", "count(*) as count", "sum(age) as sumAge", "avg(age) as avgAge",
        "max(age) as age"
      ]
  )
  fun statistics3(): List<Map<String, Any?>>

  /**
   * @param pageable
   * @return
   */
  @XmllessMethod
  fun findVOPage(
      pageable: Pageable
  ): Page<StudentVO>

  /**
   * @param form
   * @return
   */
  @XmllessMethod(
      conditions = [
        "name", "age", "userName like keywords or createUserName like keywords"
      ]
  )
  fun findByNameAndAgeAndUserNameLikeKeywordsOrCreateUserNameLikeKeywords(
      form: StudentQueryForm
  ): List<Student>

  /**
   *
   * @return
   */
  fun count(): Int

  /**
   * @param id
   * @return
   */
  fun countNameById(@Param("id") id: Int): Int

  /**
   * @return
   */
  @XmllessMethod(
      includes = ["sum(age)"]
  )
  fun selectAgeSum(): Int

  /**
   *
   * @param id
   */
  fun deleteById(@Param("id") id: String)

  /**
   *
   * @param ids
   */
  @XmllessMethod(conditions = ["id in ids"])
  fun deleteByIds(@Param("ids") ids: List<String>)

  /**
   *
   * @param name
   */
  fun deleteByName(@Param("name") name: String)

  /**
   *
   * @param id
   * @return
   */
  fun existsById(@Param("id") id: String): Boolean

  /**
   *
   * @param name
   * @return
   */
  fun existsByName(@Param("name") name: String): Boolean

  /**
   *
   * @return
   */
  fun findAll(): List<Student>

  /**
   *
   * @param page
   * @param name
   * @param subjectId
   * @return
   */
  @XmllessMethod(conditions = ["name", "subjectId"])
  fun findAllPage(
      name: String?,
      subjectId: Int?,
      @Param("pageable") page: Pageable
  ): Page<Student>

  /**
   *
   * @param pageable
   * @return
   */
  @XmllessMethod
  fun findAllPageable(@Param("pageable") pageable: Pageable): Page<Student>

  fun findCreateUserName(): List<String>

  /**
   *
   * @param min
   * @param max
   * @return
   */
  fun findByAgeBetweenMinAndMaxOrderByBirthday(
      @Param("min") min: Int,
      @Param("max") max: Int
  ): List<Student>

  /**
   * @param startTime
   * @param endTime
   * @return
   */
  fun findByCreateTimeBetweenStartTimeAndEndTime(
      @Param("startTime") startTime: LocalDateTime?, @Param("endTime") endTime: LocalDateTime?
  ): List<Student>

  /**
   * @param age
   * @param name
   * @return
   */
  @XmllessMethod(conditions = ["name", "age"])
  fun findByAge(@Param("age") age: Int, @Param("name") name: String): List<Student>

  /**
   *
   * @param age
   * @return
   */
  fun findByAgeGte(@Param("age") age: Int): List<Student>

  /**
   *
   * @param date
   * @return
   */
  @SelectedProperties(["id", "name", "createTime", "birthday"])
  fun findByBirthday(date: LocalDate): List<Student>

  /**
   *
   * @return
   */
  fun findByGraduatedEqTrue(): List<Student>

  /**
   * @return
   */
  @XmllessMethod(conditions = ["graduated = true"])
  fun findByGraduatedEqTrue2(): List<Student>

  /**
   *
   * @return
   * @param id
   */
  fun findById(@Param("id") id: String): Student?

  /**
   *
   * @param phoneNumber
   * @return
   */
  fun findByPhoneNumberLikeLeft(@Param("phoneNumber") phoneNumber: String): List<Student>


  @XmllessMethod(
      conditions = ["detail.roles contains any roles"]
  )
  fun findContainsAny(@Param("roles") roles: List<Int>): List<Student>

  @XmllessMethod(
      conditions = ["detail.height = 2"]
  )
  fun findByJsonField(): List<Student>

  @XmllessMethod(
      conditions = ["detail.roles contains role"]
  )
  fun findContainsInt(@Param("role") role: Int): List<Student>

  @XmllessMethod(
      conditions = ["detail.names contains name"]
  )
  fun findContainsString(@Param("name") name: String): List<Student>


  @XmllessMethod(
      conditions = ["detail.roles contains any detail.roles"]
  )
  fun find888(@Param("detail") detail: StudentDetail, keywords: String): List<Student>

  @XmllessMethod(
      conditions = [
        "data.height contains 2"
      ]
  )
  fun findInObjectArray(): List<Student>

  @XmllessMethod(
      type = QueryType.Update,
      conditions = [
        "id"
      ],
      updateSet = [
        "data add #data"
      ]
  )
  fun appendData(@Param("id") id: String, @Param("data") data: StudentDetail): Int

  /**
   * @param createTime
   * @return
   */
  @XmllessMethod(conditions = ["date(createTime) = date(createTime)"])
  fun findByCreateTimeEqDate(@Param("createTime") createTime: LocalDate): List<Student>

  /**
   * @param createTime
   * @return
   */
  fun findByCreateTimeEqMonth(createTime: LocalDate): List<Student>

  /**
   *
   * @param phoneNumber
   * @return
   */
  fun findByPhoneNumberLikeRight(@Param("phoneNumber") phoneNumber: String): List<Student>

  /**
   *
   * @param state
   * @return
   */
  fun findByStateIn(@Param("state") state: List<StudentState>): List<Student>

  /**
   *
   * @param subjectId
   * @return
   */
  fun findBySubjectId(@Param("subjectId") subjectId: Int): List<Student>

  /**
   *
   * @return
   */
  @JsonResult
  fun findDetail(): List<StudentDetail>

  /**
   *
   * @return
   */
  fun findId(): List<String>

  /**
   *
   * @param student
   */
  fun save(student: Student)

  /**
   *
   * @param list
   */
  fun saveAll(list: List<Student>)

  /**
   *
   * @param student
   */
  fun saveOrUpdate(student: Student)

  /**
   *
   * @param list
   */
  fun saveOrUpdateAll(list: List<Student>)

  /**
   *
   * @param student
   * @return
   */
  fun update(student: Student): Int

  /**
   *
   * @param name
   * @param id
   * @return
   */
  fun updateNameById(@Param("name") name: String, @Param("id") id: String): Int

  /**
   *
   * @param student
   * @return
   */
  @XmllessMethod(
      updateSet = ["phoneNumber = '222'", "name",
        "age = age + 1",
        "phoneNumber", "grade"]
  )
  fun updatePartly(student: Student): Int


  @XmllessMethod(type = QueryType.Update)
  fun updatePartly2(student: StudentUpdateVO): Int

  @XmllessMethod(
      updateSet = ["birthday = now()"]
  )
  fun updateBirthday(id: String): Int

  /**
   * @param form
   * @return
   */
  fun findByNameOrAge(form: QueryForm): List<Student>

  /**
   * @param form
   * @return
   */
  fun findByNameLikeAndAgeAndCreateTimeBetweenStartAndEnd(form: QueryForm): List<Student>

  /**
   * @param form
   * @param pageable
   * @return
   */
  @XmllessMethod(
      conditions = ["name like", "age", "createTime between start and end"]
  )
  fun findByNameLikeAndAgeAndCreateTimeBetweenStartAndEndPageable3(
      @Param("form") form: QueryForm, pageable: Pageable
  ): Page<Student>


  fun findByBirthdayBetween(@Param("from") from: LocalDate, @Param("to") to: LocalDate): List<Student>

  @XmllessMethod(
      conditions = [
        "birthday between from and to"
      ]
  )
  fun findByBirthdayBetween2(@Param("from") from: LocalDate, @Param("to") to: LocalDate): List<Student>


  @XmllessMethod(
      conditions = [
        "id not in ids",
        "id not in (1,2,3)",
        "grade",
        "favorites contains favorite",
        "graduated = true"
      ]
  )
  fun findByIdNotIn(
      @Param("ids") ids: List<Int>,
      favorite: String,
      grade: Int,
  ): List<Student>


  @XmllessMethod(
      conditions = [
        "detail.roles contains any roles or (createTime >= from and createTime <= to)"
      ],
  )
  fun find999(
      @Param("roles") roles: List<Int>,
      @Param("from") from: LocalDateTime,
      @Param("to") to: LocalDateTime
  ): List<Student>

  /**
   * @param form
   * @param pageable
   * @return
   */
  @XmllessMethod(conditions = ["name like", "age", "createTime between start and end"])
  fun findByNameLikeAndAgeAndCreateTimeBetweenStartAndEndPageable(
      @Param("form") form: QueryForm, pageable: Pageable
  ): Page<Student>

  @XmllessMethod(
      conditions = [
        "detail.score > #score"
      ]
  )
  fun findByScoreGt(score: Double): List<Student>

  /**
   * @param form
   * @param pageable
   * @return
   */
  @XmllessMethod(conditions = ["name like", "age", "createTime between start and end"])
  fun findByNameLikeAndAgeAndCreateTimeBetweenStartAndEndPageable2(
      @Param("form") form: QueryForm, @Param("p") pageable: Pageable
  ): Page<Student>

  /**
   * @param min
   * @param max
   * @return
   */
  fun findByAgeBetween(@Param("min") min: Int?, @Param("max") max: Int?): List<Student>

  @XmllessMethod
  fun findSimpleList(): List<StudentSimpleVO>

  /**
   * @param min
   * @param max
   * @return
   */
  fun findByAgeBetweenMinAndMax(@Param("min") min: Int?, @Param("max") max: Int?): List<Student>

  fun findByDataContains(@Param("data") data: Map<String, Int>): List<Student>

  class GroupResult {

    @PropertyMapping("count(*)")
    var count: Int = 0
    var grade: Int? = null

  }

}


